#include "captureserver.h"
#include "qavframe.h"
#include <QDebug>
#include <QByteArray>
#include <QCryptographicHash>
extern "C" {
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}
#include <QCryptographicHash>
#include "capture/capturefactory.h"

CaptureServer::CaptureServer(int id, QObject *parent) : QObject(parent), m_pts(0), m_keyFrame(false)
{
    m_window = CaptureFactory::fromWinId(id);
    initEncoder();
    connect(&m_timer, &QTimer::timeout, this, &CaptureServer::taskShot);
}

CaptureServer::~CaptureServer()
{
    delete m_window;
    delete m_ctx;
    sws_freeContext(m_sws);
}

ScreenCapture* CaptureServer::window()
{
    return m_window;
}

void CaptureServer::initEncoder()
{
    int ret;
    int width = m_window->width();
    int height = m_window->height();
    qInfo()<<QString("捕获屏幕%0 宽:%1 高:%2").arg(m_window->winId()).arg(width).arg(height);
    AVPixelFormat srcPixFormat = m_window->pixelFormat();
    AVPixelFormat desPixFormat = AV_PIX_FMT_YUV420P;
    m_ctx = QAVCodecContext::fromEncoderId(AV_CODEC_ID_H264);
    if(m_ctx == nullptr)
    {
        return;
    }
    m_ctx->setPictureSize(width, height);
    m_ctx->setTimeBase(1, 20);
    m_ctx->setFramerate(20, 1);
    m_ctx->setMaxBFrames(0);
    m_ctx->setPixFmt(AV_PIX_FMT_YUV420P);
    m_ctx->setOpt("preset", "veryfast", 0);
    m_ctx->setOpt("tune", "zerolatency", 0);
    ret = m_ctx->open2();
    if (ret < 0)
    {
        qCritical()<<"Could not open codec: "<< ret;
        return;
    }

    m_sws = sws_getContext(width, height, srcPixFormat
                           , width, height, desPixFormat
                           , SWS_POINT, nullptr, nullptr, nullptr);
    m_pts = 0;
}

void CaptureServer::startMs(std::chrono::milliseconds msec)
{
    m_timer.start(msec);
}

void CaptureServer::requestOneKeyFrame()
{
    m_keyFrame = true;
}

void CaptureServer::start()
{
    qDebug()<<"开始";
    this->requestOneKeyFrame();
    m_timer.start();
}

void CaptureServer::stop()
{
    qDebug()<<"停止";
    m_timer.stop();
}

void CaptureServer::taskShot()
{
    int ret;
    m_window->updateFramebuffer();
    QByteArray buff = m_window->getFramebuffer();
    AVFrame *srcFrame = av_frame_alloc();
    srcFrame->width = m_window->width();
    srcFrame->height = m_window->height();
    srcFrame->format = m_window->pixelFormat();
    ret = av_frame_get_buffer(srcFrame, 32);
    if (ret < 0) {
        qCritical()<<"Could not allocate the video srcframe data";
        return;
    }

    AVFrame *destFrame = av_frame_alloc();
    destFrame->width = m_window->width();
    destFrame->height = m_window->height();
    destFrame->format = AV_PIX_FMT_YUV420P;
    ret = av_frame_get_buffer(destFrame, 32);
    if (ret < 0) {
        qCritical()<<"Could not allocate the video dstframe data";
        return;
    }

    srcFrame->pts = m_pts;
    destFrame->pts = m_pts;
    m_pts++;

    AVPacket *pkt = av_packet_alloc();

    m_window->getFramebuffer(reinterpret_cast<char*>(srcFrame->data[0]));
    QByteArray arr = QByteArray(reinterpret_cast<char*>(srcFrame->data[0]), m_window->width()*m_window->height()*4);
    QByteArray md5 = QCryptographicHash::hash(QByteArray(reinterpret_cast<char*>(srcFrame->data[0]), m_window->width()*m_window->height()*4), QCryptographicHash::Md5);

    if(m_md5==md5)
    {
        av_frame_free(&srcFrame);
        av_frame_free(&destFrame);
        return;
    }
    m_md5=md5;
    sws_scale(m_sws, srcFrame->data, srcFrame->linesize, 0, srcFrame->height, destFrame->data, destFrame->linesize);
    if(m_keyFrame)
    {
        m_keyFrame=false;
        destFrame->pict_type = AV_PICTURE_TYPE_I;
    }
    m_ctx->sendFrame(destFrame);
    while(true)
    {
        int ret = m_ctx->receivePacket(pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            break;
        }
        else if (ret < 0)
        {
            qCritical()<< "Error during encoding";
            break;
        }
        QByteArray data(reinterpret_cast<char*>(pkt->data), pkt->size);
        emit outputStream(data);
    }

    av_frame_free(&srcFrame);
    av_frame_free(&destFrame);
}
